/* Copyright 2007 dnAnalytics Project.
 *
 * Contributors to this file:
 * Patrick van der Velde
 * Marcus Cuda - http://marcus.cuda.net
 *
 * This file is part of dnAnalytics.  dnAnalytics is licensed under the 
 * Microsoft Public License. See License.txt for a complete copy of the
 * license.
 */
using System;
using dnAnalytics.Properties;

namespace dnAnalytics.LinearAlgebra
{
    /// <summary>
    /// A matrix class using dense storage.
    /// </summary>
    public partial class DenseMatrix
    {
        /// <summary>Calculates the L1 norm.</summary>
        /// <returns>The L1 norm of the matrix.</returns>
        public override double L1Norm()
        {
            return SafeNativeMethods.d_matrix_norm('1', Rows, Columns, mData);
        }


        /// <summary>Calculates the Frobenius norm of this matrix.</summary>
        /// <returns>The Frobenius norm of this matrix.</returns>
        public override double FrobeniusNorm()
        {
            return SafeNativeMethods.d_matrix_norm('F', Rows, Columns, mData);
        }

        /// <summary>Calculates the infinity norm of this matrix.</summary>
        /// <returns>The infinity norm of this matrix.</returns>   
        public override double InfinityNorm()
        {
            return SafeNativeMethods.d_matrix_norm('I', Rows, Columns, mData);
        }

 
        /// <summary>
        /// Adds another matrix to this matrix.
        /// </summary>
        /// <param name="other">The matrix to add to this matrix.</param>
        /// <exception cref="ArgumentNullException">If the other matrix is <strong>null</strong>.</exception>
        /// <exception cref="NotConformableException">If the two matrices don't have the same dimensions.</exception>
        public override void Add(Matrix other)
        {
            if (other == null)
            {
                throw new ArgumentNullException("other");
            }

            if (other.Rows != Rows || other.Columns != Columns)
            {
                throw new NotConformableException("other", Resources.ParameterNotConformable);
            }

            DenseMatrix dense = other as DenseMatrix;
            if (dense == null)
            {
                base.Add(other);
            }
            else
            {
                SafeNativeMethods.d_vector_add(mData.Length, mData, dense.mData, mData);
            }
        }

        /// <summary>
        /// Negates each element of this matrix.
        /// </summary>        
        public override void Negate()
        {
            SafeNativeMethods.d_scale(mData.Length, -1.0, mData);
        }

        /// <summary>
        /// Subtracts another matrix from this matrix.
        /// </summary>
        /// <param name="other">The matrix to subtract.</param>
        /// <exception cref="ArgumentNullException">If the other matrix is <strong>null</strong>.</exception>
        /// <exception cref="NotConformableException">If the two matrices don't have the same dimensions.</exception>
        public override void Subtract(Matrix other)
        {
            if (other == null)
            {
                throw new ArgumentNullException("other");
            }

            if (other.Rows != Rows || other.Columns != Columns)
            {
                throw new NotConformableException("other", Resources.ParameterNotConformable);
            }

            DenseMatrix dense = other as DenseMatrix;
            if (dense == null)
            {
                base.Add(other);
            }
            else
            {
                SafeNativeMethods.d_vector_subtract(mData.Length, mData, dense.mData, mData);
            }
        }

        /// <summary>
        /// Multiplies each element of this matrix with a scalar.
        /// </summary>
        /// <param name="scalar">The scalar to multiply with.</param>
        public override void Multiply(double scalar)
        {
            SafeNativeMethods.d_scale(mData.Length, scalar, mData);
        }

        /// <summary>
        /// Multiplies this matrix with another matrix and places the results into the result matrix.
        /// </summary>
        /// <param name="other">The matrix to multiply with.</param>
        /// <param name="result">The result of the multiplication.</param>
        /// <exception cref="ArgumentNullException">If the other matrix is <see langword="null" />.</exception>
        /// <exception cref="ArgumentNullException">If the result matrix is <see langword="null" />.</exception>
        /// <exception cref="NotConformableException">If <strong>this.Columns != other.Rows</strong>.</exception>
        /// <exception cref="NotConformableException">If the result matrix's dimensions are not the this.Rows x other.Columns.</exception>
        public override void Multiply(Matrix other, Matrix result)
        {
            if (other == null)
            {
                throw new ArgumentNullException("other");
            }

            if (result == null)
            {
                throw new ArgumentNullException("result");
            }

            if (Columns != other.Rows)
            {
                throw new NotConformableException("other", Resources.ParameterNotConformable);
            }

            if (result.Rows != Rows || result.Columns != other.Columns)
            {
                throw new NotConformableException("result", Resources.ParameterNotConformable);
            }

            DenseMatrix denseOther = other as DenseMatrix;
            DenseMatrix denseResult = result as DenseMatrix;
            if (denseOther == null || denseResult == null)
            {
                base.Multiply(other, result);
            }
            else
            {
                SafeNativeMethods.d_matrix_multiply(Rows, other.Columns, Columns, mData, denseOther.mData, denseResult.mData);
            }
        }

        /// <summary>
        /// Multiplies this matrix by a vector and returns the result.
        /// </summary>
        /// <param name="rightSide">The vector to multiply with.</param>
        /// <param name="result">The result of the multiplication.</param>
        /// <exception cref="ArgumentNullException">If rightSide is <strong>null</strong>.</exception>
        /// <exception cref="NotConformableException">If <c>this.Columns != rightSide.Size</c>.</exception>
        public override void Multiply(Vector rightSide, Vector result)
        {
            if (rightSide == null)
            {
                throw new ArgumentNullException("rightSide");
            }

            if (Columns != rightSide.Count)
            {
                throw new NotConformableException("rightSide", Resources.ParameterNotConformable);
            }

            if (result == null)
            {
                throw new ArgumentNullException("result");
            }

            if (Rows != result.Count)
            {
                throw new NotConformableException("result", Resources.ParameterNotConformable);
            }

            DenseVector denseOther = rightSide as DenseVector;
            DenseVector denseResult = result as DenseVector;
            if (denseOther == null || denseResult == null)
            {
                base.Multiply(rightSide, result);
            }
            else
            {
                SafeNativeMethods.d_matrix_multiply(Rows, 1, Columns, mData, denseOther.Data, denseResult.Data);
            }
        }

        /// <summary>
        /// Multiplies this matrix by a vector and returns the result. The operation is vector * matrix.
        /// </summary>
        /// <param name="leftSide">The vector to multiply with.</param>
        /// <param name="result">The result of the multiplication.</param>
        /// <exception cref="ArgumentNullException">If leftSide is <strong>null</strong>.</exception>
        /// <exception cref="NotConformableException">If <strong>this.Rows != leftSide.Size</strong>.</exception>
        public override void LeftMultiply(Vector leftSide, Vector result)
        {
            if (leftSide == null)
            {
                throw new ArgumentNullException("leftSide");
            }

            if (Rows != leftSide.Count)
            {
                throw new NotConformableException("leftSide", Resources.ParameterNotConformable);
            }

            if (result == null)
            {
                throw new ArgumentNullException("result");
            }

            if (Columns != result.Count)
            {
                throw new NotConformableException("result", Resources.ParameterNotConformable);
            }

            DenseVector denseOther = leftSide as DenseVector;
            DenseVector denseResult = result as DenseVector;
            if (denseOther == null || denseResult == null)
            {
                base.LeftMultiply(leftSide, result);
            }
            else
            {
                SafeNativeMethods.d_matrix_multiply(1, Columns, Rows, denseOther.Data, mData, denseResult.Data);
            }
        }
    }
}